package com.framework.service.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.framework.common.Enums.UserTypeEnum;
import com.framework.common.constant.Constants;
import com.framework.common.domain.WechatMessage;
import com.framework.common.util.MessageUtil;
import com.framework.common.util.RedisUtils;
import com.framework.service.core.dao.mapper.ThirdUserInfoMapper;
import com.framework.service.core.dao.model.ThirdUserInfo;
import com.framework.service.core.service.IWeixinService;
import com.framework.core.api.response.WxGeneralToken;
import com.framework.core.api.response.WxUserInfo;
import com.framework.core.api.response.WxUserOpenIdRes;
import com.framework.service.core.dao.mapper.ThirdUserInfoMapper;
import com.framework.service.core.dao.model.ThirdUserInfo;
import com.framework.service.core.service.IWeixinService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Base64;
import java.util.Date;

/**
 * @author zhoubb
 * @version V1.0
 * @Date 2020/4/12 21:54
 * @since JDK 1.8
 */
@Service
@Slf4j
public class WeixinServiceImpl implements IWeixinService {

    /** 微信api认证地址*/
    @Value("${wechat.weixinApiUrl_jscode2session}")
    private String weixinApiUrl_jscode2session;

    @Value("${wechat.weixinApiUrl_token}")
    private String weixinApiUrl_token;

    @Value("${wechat.weixinApiUrl_userinfo}")
    private String weixinApiUrl_userinfo;

    @Value("${wechat.ttz.appid}")
    private String wechat_ttz_appid;

    @Value("${wechat.ttz.secret}")
    private String wechat_ttz_secret;

    @Value("${wechat.tf.appid}")
    private String wechat_tf_appid;

    @Value("${wechat.tf.secret}")
    private String wechat_tf_secret;

    @Value("${wechat.official.appid}")
    private String wechat_official_appid;

    @Value("${wechat.official.secret}")
    private String wechat_official_secret;

    @Value("${wechat.officialUrl_token}")
    private String wechat_officialUrl_token;

    @Value("${wechat.officialUrl_userinfo}")
    private String wechat_officialUrl_userinfo;

    @Value("${wechat.official_content}")
    private String wechat_official_content;

    @Value("${spring.profiles.active}")
    private String profiles;

    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private ThirdUserInfoMapper thirdUserInfoMapper;

    @Override
    public WxUserOpenIdRes getWeixinOpenidAndKey(String code) {
        if(StringUtils.isBlank(code)) {
            log.warn("获取微信用户openId，参数为空!code=[{}]", code);
            return null;
        }
        log.info("开始获取微信用户openId,code=[{}]", code);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> request = new HttpEntity<String>(headers);
        // 组装微信api地址
        StringBuffer serverUrl = new StringBuffer(
                weixinApiUrl_jscode2session.replace("#APPID",wechat_ttz_appid)
                        .replace("#SECRET", wechat_ttz_secret)).append(code);
        ResponseEntity<String> responseEntity = null;
        try{
            responseEntity = callWeinApi(serverUrl.toString());
        } catch (Exception e) {
            log.error("调用微信api接口获取用户openId异常!", e);
            return null;
        }

        log.info("请求微信api获取用户openId完成,响应结果=[{}]", JSON.toJSONString(responseEntity));
        if (!HttpStatus.OK.equals(responseEntity.getStatusCode())) {
            log.warn("调用微信api接口获取用户openId失败, statusCode=[{}]", responseEntity.getStatusCode());
            return null;
        }
         WxUserOpenIdRes wxUserOpenIdRes = JSON.parseObject(responseEntity.getBody(), WxUserOpenIdRes.class);
        if(StringUtils.equals("dev", profiles)) {
            wxUserOpenIdRes = new WxUserOpenIdRes("session_key", "unionid", "open_id", null, 0, "success");
        }
        return wxUserOpenIdRes;
    }

    @Override
    public WxGeneralToken getWeixinAccessToken(String userType) {
        if(StringUtils.isBlank(userType)) {
            log.info("获取微信token,参数[userType]为空");
            return null;
        }
        log.info("开始获取微信token,userType=[{}]", userType);
        // 组装微信api地址
        StringBuffer serverUrl = new StringBuffer(
                weixinApiUrl_token.replace("#APPID",wechat_ttz_appid)
                        .replace("#SECRET", wechat_ttz_secret));
        if(StringUtils.equals(UserTypeEnum.TOURIST.getCode(), userType)) {
            serverUrl = new StringBuffer(
                    weixinApiUrl_token.replace("#APPID",wechat_tf_appid)
                            .replace("#SECRET", wechat_ttz_secret));
        }
        ResponseEntity<String> responseEntity = null;
        try{
            responseEntity = callWeinApi(serverUrl.toString());
        } catch (Exception e) {
            log.error("调用微信api接口微信token异常!", e);
            return null;
        }
        log.info("请求微信api获取微信token完成,响应结果=[{}]", JSON.toJSONString(responseEntity));
        if (!HttpStatus.OK.equals(responseEntity.getStatusCode())) {
            log.warn("调用微信api接口获取微信token失败, statusCode=[{}]", responseEntity.getStatusCode());
            return null;
        }
        WxGeneralToken wxGeneralToken = JSON.parseObject(responseEntity.getBody(), WxGeneralToken.class);
        return wxGeneralToken;
    }

    @Override
    public WxUserInfo getWeixinUserInfo(String accessToken,String openId) {
        if(StringUtils.isBlank(openId)) {
            log.info("获取微信token,参数[openId]为空");
            return null;
        }
        log.info("开始获取微信用户信息,openId=[{}]", openId);
        // 组装微信api地址
        StringBuffer serverUrl = new StringBuffer(
                weixinApiUrl_userinfo.replace("#ACCESS_TOKEN",accessToken)
                        .replace("#OPENID", openId));
        ResponseEntity<String> responseEntity = null;
        try{
            responseEntity = callWeinApi(serverUrl.toString());
        } catch (Exception e) {
            log.error("调用微信api接口微信token异常!", e);
            return null;
        }
        log.info("请求微信api获取微信用户信息完成,响应结果=[{}]", JSON.toJSONString(responseEntity));
        if (!HttpStatus.OK.equals(responseEntity.getStatusCode())) {
            log.warn("调用微信api接口获取微信用户信息失败, statusCode=[{}]", responseEntity.getStatusCode());
            return null;
        }
         WxUserInfo wxUserInfo = JSON.parseObject(responseEntity.getBody(), WxUserInfo.class);
        if(StringUtils.equals("dev", profiles)) {
            wxUserInfo = new WxUserInfo("oGc-A4qbP1kLxtzWBqIrfIozh9gw", "冰涟~", "1", "浙江", "杭州", "CN", "", "","");
        }
        return wxUserInfo;
    }

    @Override
    public String getPhoneNumberFromWeixin(String encryptedData, String sessionKey, String iv) {
        if(StringUtils.isBlank(encryptedData) || StringUtils.isBlank(sessionKey) || StringUtils.isBlank(iv)) {
            log.warn("解密微信用户手机号,参数为空");
            return null;
        }
        log.info("开始解密微信用户手机号,encryptedData=[{}]", encryptedData);
        // 被加密的数据
        byte[] dataByte = Base64.getDecoder().decode(encryptedData);
        // 加密秘钥
        byte[] keyByte = Base64.getDecoder().decode(sessionKey);
        // 偏移量
        byte[] ivByte = Base64.getDecoder().decode(iv);
        try {
            // 如果密钥不足16位，那么就补足.  这个if 中的内容很重要
            int base = 16;
            if (keyByte.length % base != 0) {
                int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
                byte[] temp = new byte[groups * base];
                Arrays.fill(temp, (byte) 0);
                System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
                keyByte = temp;
            }
            // 初始化
            Security.addProvider(new BouncyCastleProvider());
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
            SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
            AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
            parameters.init(new IvParameterSpec(ivByte));
            cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
            byte[] resultByte = cipher.doFinal(dataByte);
            if (null != resultByte && resultByte.length > 0) {
                String result = new String(resultByte, "UTF-8");
                log.info("解密微信用户手机号完成,结果=[{}]", result);
                return JSON.parseObject(result).getString("phoneNumber");
            }
        } catch (Exception e) {
            log.error("解密微信用户手机号出现异常！", e);
        }
        return null;
    }

    @Override
    public String subscribe(WechatMessage wechatMessage) {
        /**
         *1、用openId拿到unionId，然后更新至数据库
         *2、回复关注消息
         */
        if(wechatMessage == null) {
            log.warn("微信公众号订阅事件处理失败，参数为空");
        }
        log.info("开始处理微信公众号订阅事件,openId=[{}]", wechatMessage.getFromUserName());
        log.info("开始处理微信公众号access_token");
        // 检查redis是否存在access_token
        String accessToken = null;
        if(redisUtils.hasKey(Constants.WECHAT_OFFICIAL_ACCESS_TOKEN_KEY) && redisUtils.get(Constants.WECHAT_OFFICIAL_ACCESS_TOKEN_KEY) != null) {
            accessToken = redisUtils.get(Constants.WECHAT_OFFICIAL_ACCESS_TOKEN_KEY);
        } else {
            // 调用api获取token
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
            HttpEntity<String> request = new HttpEntity<String>(headers);
            // 组装微信api地址
            StringBuffer serverUrl = new StringBuffer(
                    wechat_officialUrl_token.replace("#APPID",wechat_official_appid)
                            .replace("#APPSECRET", wechat_official_secret));
            ResponseEntity<String> responseEntity = null;
            try{
                responseEntity = callWeinApi(serverUrl.toString());
            } catch (Exception e) {
                log.error("调用微信公众号api接口获取accessToken异常!", e);
                return null;
            }
            log.info("调用微信公众号api接口获取accessToken完成,响应结果=[{}]", JSON.toJSONString(responseEntity));
            if (!HttpStatus.OK.equals(responseEntity.getStatusCode())) {
                log.warn("调用微信公众号api接口获取accessToken失败, statusCode=[{}]", responseEntity.getStatusCode());
                return null;
            }
            WxGeneralToken wxGeneralToken = JSON.parseObject(responseEntity.getBody(), WxGeneralToken.class);
            if(wxGeneralToken == null) {
                log.warn("调用微信公众号api接口获取accessToken失败");
                return null;
            }
            accessToken = wxGeneralToken.getAccess_token();
            if(accessToken == null) {
                log.warn("调用微信公众号api接口获取accessToken失败, access_token为空");
                return null;
            }
            redisUtils.set(Constants.WECHAT_OFFICIAL_ACCESS_TOKEN_KEY, accessToken, 7200);
        }

        // 获取用户信息
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> request = new HttpEntity<String>(headers);
        // 组装微信api地址
        StringBuffer serverUrl = new StringBuffer(
                wechat_officialUrl_userinfo.replace("#ACCESS_TOKEN",accessToken)
                        .replace("#OPENID", wechatMessage.getFromUserName()));
        ResponseEntity<String> responseEntity = null;
        try{
            responseEntity = callWeinApi(serverUrl.toString());
        } catch (Exception e) {
            log.error("调用微信公众号api接口获取用户信息异常!", e);
            return null;
        }
        log.info("调用微信公众号api接口获取用户信息完成,响应结果=[{}]", JSON.toJSONString(responseEntity));
        if (!HttpStatus.OK.equals(responseEntity.getStatusCode())) {
            log.warn("调用微信公众号api接口获取用户信息失败, statusCode=[{}]", responseEntity.getStatusCode());
            return null;
        }
        WxUserInfo wxUserInfo = JSON.parseObject(responseEntity.getBody(), WxUserInfo.class);

        if(StringUtils.isNotBlank(wxUserInfo.getUnionid())) {
            // 将unionId和openId存入redis
            redisUtils.setNotExpire(Constants.WECHAT_OFFICIAL_OPEN_ID_KEY.concat(wxUserInfo.getUnionid()), wxUserInfo.getOpenid());
            // 更新thirdinfo
            ThirdUserInfo thirdUserInfo = new ThirdUserInfo();
            thirdUserInfo.setOfficialOpenId(wxUserInfo.getOpenid());
            thirdUserInfo.setUnionId(wxUserInfo.getUnionid());
            thirdUserInfoMapper.updateByUnionId(thirdUserInfo);
        }

        // 返回自动回复内容
        WechatMessage returnMessage = new WechatMessage();
        returnMessage.setFromUserName(wechatMessage.getToUserName());
        returnMessage.setToUserName(wechatMessage.getFromUserName());
        returnMessage.setMsgId(String.valueOf(new Date().getTime()));
        returnMessage.setMsgType("text");
        returnMessage.setContent(wechat_official_content);
        returnMessage.setCreateTime(new Date().getTime());
        log.info("微信公众号订阅事件处理完成,openId=[{}]", wechatMessage.getFromUserName());
        return MessageUtil.textMessageToXml(returnMessage);
    }


    /**
     * 调用微信api
     * @param apiUrl
     * @return
     */
    private ResponseEntity<String> callWeinApi(String apiUrl) {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
        HttpEntity<String> request = new HttpEntity<String>(headers);
        log.info("开始请求微信api,url=[{}]", apiUrl);
        return restTemplate.exchange(apiUrl, HttpMethod.GET, request, String.class);
    }
}
